-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Don't access items in empty cache #2332
Conversation
@lmoureaux Would you like to review this PR? It looks good to me and seems to fix the issue reported. |
In particular, I could imagine that a solution along these lines would be better, because it effectively continues to use the cache. output_type_iterate(stat_index)
{
int amount = 0;
if (pcsoutputs && !pcsoutputs->empty())
{
amount = pcsoutputs->at(sp)[stat_index];
}
if (!pcsoutputs)
{
amount = get_specialist_output(pcity, sp, stat_index);
}
output[stat_index] += count * amount;
} However, I was not sure how to interpret the empty cache. |
Yes, I want to check where the empty cache comes from. Need to check the code again ^^ |
The cache is not created when the governor settings forbid specialists. Stack trace of the crash:
|
Governors that forbid specialists do not bother computing how much specialists would produce and end up with an empty specialist output cache. When applying solutions to evaluate them, however, we were trying to use the cached values. Only do it when there specialists are actually present in the solution. See longturn#2332.
Governors that forbid specialists do not bother computing how much specialists would produce and end up with an empty specialist output cache. When applying solutions to evaluate them, however, we were trying to use the cached values. Only do it when there specialists are actually present in the solution. See longturn#2332.
#2355 addresses the root cause but in a less defensive way. |
Governors that forbid specialists do not bother computing how much specialists would produce and end up with an empty specialist output cache. When applying solutions to evaluate them, however, we were trying to use the cached values. Only do it when there specialists are actually present in the solution. See #2332.
Even after #2355 we may want to keep checking that the cache isn't empty. I'm only mildly optimistic that the issue won't appear in a different code path. |
Apply the same defensive measure, we applied to specialists output cache, to the waste cache.
225b4ce
to
0daf5f5
Compare
common/city.cpp
Outdated
@@ -2247,6 +2247,9 @@ void add_specialist_output( | |||
// This is more than just an optimization. For governors that forbid | |||
// specialists, the cache may not be filled. | |||
if (count > 0) { | |||
// If there is a cache it must not be empty. | |||
fc_assert(!pcsoutputs || !pcsoutputs->empty()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fc_
asserts aren't fatal by default. Use fc_assert_action(condition, continue)
to skip this specialist, which is probably an ok fallback behavior:
fc_assert(!pcsoutputs || !pcsoutputs->empty()); | |
fc_assert_action(!pcsoutputs || !pcsoutputs->empty(), continue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I implemented another solution, falling back to the old behaviour by setting the cache to nullptr
if the assertion triggers. This makes sure, that the game will behave correctly. What do you think?
common/city.cpp
Outdated
@@ -2876,6 +2879,9 @@ void set_city_production(struct city *pcity, | |||
* on, so shield waste will include shield bonuses. */ | |||
output_type_iterate(o) | |||
{ | |||
// If there is a cache it must not be empty. | |||
fc_assert(!pcwaste || !pcwaste->empty()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably go before the loop to avoid getting 6 messages every time. I think returning if the assert fails is fine, so use fc_assert_ret(condition)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
Fallback to the behavior used prior to the introduction of these caches, if the assertion checking for an empty cache is triggered. While here, move an assertion outside a loop to make sure it is not checked for each iteration.
5783403
to
9ed47c0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a nicer solution!
While debugging an issue reported in LT85, I found that in certain circumstances an empty specialist output cache (introduced in 5c44029) is accessed, what crashed the client.
An obvious fix, contained in this PR, is limiting access to non-empty caches. There may be better ways to handle this though.
While here, I applied the same safety measure to the waste level cache, introduced in ecabf5d.